home *** CD-ROM | disk | FTP | other *** search
/ Inside Mac Games Volume 4 #4 / IMG 36 April 1996.iso / IMG April 1996 / IMG April 1996.rsrc / TEXT_144.txt < prev    next >
Text File  |  1996-04-23  |  11KB  |  113 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17. by Jamie McCornack
  18.  
  19.  
  20. Brain Drain
  21. How nice it is to have a support group! There‚Äôs a bug in the MGW1Code package; in MGWGraphics1.c, to be specific.
  22.  
  23. Since MGW was supposed to be a training package (developed as introductory material for Tricks of the Mac Game Programming Gurus, published by Hayden Books), as a familiarizer for the basic concepts of game programming, and since this bug doesn‚Äôt manifest itself in simple programs (like the many and varied versions of HelloWorld you‚Äôve been getting in this column), it was easy to live with, ignore, overlook, and‚Ķwell, frankly, I didn‚Äôt even know it was there until readers pointed it out to me.
  24. But since MGW is out on the streets, it is being used as the engine (to use the term very loosely) for some genuine games. This delights me, natch, except for one thing: some of the folks writing rather complex genuine games with MGW have discovered that (blush) Yours Truly wrote some significant errors into the graphics routines. Significant as in Fatal Error and Bomb Box and ‚Äúthe application quit unexpectedly‚Ķ‚Äù alerts.
  25.  
  26. What is the bug? Let me quote some e-mail I got from Michael Andersson, who started off with some kind words like ‚Äúexcellent book,‚Äù before commenting re MGWGraphics1.c version 1.0.0‚Ķ
  27.  
  28. ‚ÄúIt seems there is a bug in it, though. The call to KillOffscreenPixMap() (and probably the b&w one as well) does not release the memory properly. Creating and removing a lot of offscreens will fill the available memory pretty quickly.‚Äù
  29. Well, that‚Äôs pretty straightforward. It‚Äôs sure a lot easier to understand than a comment written with THE CAPS LOCK KEY HELD DOWN AND LOTS OF EXCLAMATION POINTS!!!!!!!, not to mention distracting references re my mom (wow, I never knew she did that, but nevertheless, let‚Äôs get back to the subject of game programming), but hey, it doesn‚Äôt sound too bad. ‚ÄúAvailable memory‚Äù is a relative term, and some quite decent games have modest memory requirements. Dark Castle (unsolicited plug: now available in color from Delta Tao) runs great on a 1-meg Mac Plus, and though my own wetware memory is losing reliability as the decades flow, I seem to recall the original Lode Runner running on a 32K Apple II. So what‚Äôs the big deal about not releasing memory properly?
  30.  
  31. Let‚Äôs look at what KillOffscreenPixMap() is supposed to do.
  32.  
  33. CreateOffscreenPixMap() allocates memory as needed for the size and depth of the desired offscreen pixel map. If you want to make a pixel map that is 640 by 480, you‚Äôd pass CreateOffscreenPixMap() a rect parameter of that size, which is 307,200 individual pixels (640 times 480), and if you‚Äôre running 8-bit color (eight bits is one byte) your pixmap requires about 30K bytes of RAM. No big deal, right?
  34. When your program is done with that particular pixmap, calling KillOffscreenPixMap() frees that memory for further use. Hah! It‚Äôs supposed to, but it doesn‚Äôt!
  35.  
  36. You could go years without this being a problem, if all your backgrounds for all your levels were the same size, presuming you used one offscreen workMap and one offscreen backgroundMap, and instead of killing the old maps and allocating new maps with each level change, you kept the maps and used CopyBits() to replace the previous level‚Äôs data with the new level‚Äôs data. Or you could use a single background and change levels by placing new platforms, images, etceteras, against a common background, a la john calhoun‚Äôs Glypha III. Those of you who have Glypha III source code (a fully commented version comes on the Tricks of the Mac Game Programming Gurus CD, and there‚Äôs a standard version floating around on various on-line services) will note that while it includes a CreateOffscreenPixMap() routine (which I pretty much used word-for-word in MGWGraphics1.c, which is one reason john‚Äôs name is in the copyright notice), it doesn‚Äôt bother with KillOffscreenPixMap() at all. All Glypha III‚Äôs pixmaps are used throughout the game, and they get ‚Äúkilled‚Äù when you exit the program.
  37.  
  38. You, the programmer, don‚Äôt have to worry much about where the memory goes after the user hits Quit. The MacOS will take care of freeing up memory after your program is gone. But how memory is used while your program is running, well, that may require your attention.
  39.  
  40. It‚Äôs time to quote another reader, Kevin Teich this time. I‚Äôm going to quote Kevin at length because his analysis and solution are pretty right on‚Äîbetter than mine, for sure‚Äîso you can think of him as a guest author for this month‚Äôs column.
  41.  
  42. ‚Äú‚Ķmessing around with the MGWGraphics1.c file in chapter 0, attempting to adapt it to one of my own programs...[which] uses 16 source pictures to compile a composite picture to draw onto a window. Before, I had it set up as 16 GWorlds and a buffer GWorld for the composite picture...anyway though, I was attempting to change it to 16 pixmaps instead‚Ķin using CreateOffscreenPixMap and KillOffscreenPixMap to make and dispose of the 16 pixmaps, I noticed a serious memory leak. In loading a new map, the 16 pixmaps are disposed of and reallocated with new picture data, [and the routine] that Killed them wasn't disposing of them correctly.‚Äù
  43.  
  44. Okay, if you‚Äôre using 16 offscreen pixmaps as source pictures, and replacing all 16 with new pixmaps at each level change, you‚Äôre probably going to notice something is wrong. Kevin didn‚Äôt mention how much time he spent screaming and tearing handfuls of hair out of his head, but I‚Äôd suspect some of that went on before he concluded that KillOffscreenPixMap() wasn‚Äôt performing as advertised.
  45.  
  46. However, it could have been much worse. What if he‚Äôd only been using eight offscreen pixmaps, and two or three levels? He might have had a sneaky memory leak problem instead of an obvious one; a memory leak that almost overflowed the stack, but not quite, so that something else might have seemed to be the culprit.
  47.  
  48. For example, it might not have left enough memory for sound resources. This is hypothetical, but imagine a set of ‚Äòsnd ‚Äô resources that include a 200K music loop, a bunch of 10K to 20K clanks and yelps, and a 40K death moan. The game starts, and up comes the splash screen with the music loop playing in the background, and every thing seems groovy. When the game itself begins, the pixmap for the splash screen is disposed of (hah, but it‚Äôs not, because of the bug, so some memory leaks away), and the game begins, with the player‚Äôs character clanking and yelping and even death moaning when appropriate (you get three lives in this game), working up to Level 5, by which time the bug in KillOffscreenPixMap() has leaked off all but 30K of the available RAM.
  49.  
  50. Now there are little random pauses during the clanking and yelping parts of the game, because sometimes, when a clank is called for, the memory manager has to purge a yelp, and then access the hard drive to load the clank snd resource into the RAM recently vacated by said yelp. Kind of mysterious, eh? Must be a sound problem.
  51. And when the player‚Äôs character is killed, as soon as the death blow is dealt, the game crashes. Another mystery. This didn‚Äôt happen on Level 4, I wonder what we did wrong on Level 5?
  52.  
  53. End of hypothetical sidebar. Back to Kevin, who had the good fortune of a real straightforward memory leak problem, which he traced back to MGWGraphics1.c.
  54. ‚ÄúUsing zone ranger, I figured that each Create allocated 78304 bytes. Kill only freed 1072. So I figured out that CloseCPort doesn't dispose of the HandToHanded CTable, which is what is done in Create. Adding in a DisposeCTable call to Kill freed another 2144, but that still left 75000 unaccounted for. my pixmap is 300 pixels by 250 pixels - doing your rowbytes calculation, I figured out this was the amount of memory allocated to the pixmap in Create. So, my guess is that Kill isn't disposing the pixmap properly.‚Äù
  55.  
  56. Brilliant, Holmes. So I started up Zone Ranger from the Other Metrowerks Tools folder in my CodeWarrior folder, and by golly, Kevin was right!
  57.  
  58. So we give Inside Mac: Imaging a look, and find that CloseCPort, ‚Äú‚Ķalso disposes of the graphic port's pixel map, but it doesn't dispose of the pixel map's color table‚Ķ‚Äù which explains why the color table is still in memory, but why isn‚Äôt the pixel map gone? Beats me. The data may be gone, but the memory lingers on. CloseCPort releases memory used in the visRgn, clipRgn, bkPixPat, pnPixPat, fillPixPat, and grafVars but doesn't do diddly to the portPixMap, not as far as I can see. We‚Äôll call it One of the Mac‚Äôs Many Mysteries for the moment.
  59.  
  60. So here‚Äôs the fix, or at least <a> fix. In version 1.0.0, that routine used to read like this:
  61.  
  62. void KillOffscreenPixMap (CGrafPtr *wasPort)
  63.  
  64. {
  65.  if (wasPort != nil)
  66.   {
  67.    CloseCPort(*wasPort);
  68.    *wasPort = nil;
  69.   }
  70. }
  71.  
  72. The New and Improved version is as follows:
  73.  
  74. void KillOffscreenPixMap (CGrafPtr *wasPort)
  75.  
  76. {
  77.  if (*wasPort != nil)
  78.   {
  79.     // Release the data in wasPort.portPixMap
  80.     // since CloseCPort won't do it for us.
  81.    DisposePtr(((**(*wasPort)->portPixMap)).baseAddr);
  82.     // Get rid of its ColorTable since we gave
  83.     // it its own separate copy.
  84.    DisposeCTable((*(*wasPort)->portPixMap)->pmTable);
  85.     // And then, close wasPort.
  86.    CloseCPort(*wasPort);
  87.    *wasPort = nil;
  88.   }
  89. }
  90.  
  91. The black&white single-bit routine got a similar treatment (and remember, you have to use a bitmap if you want to use CopyMask()), like so:
  92.  
  93. void KillOffscreenBitMap (GrafPtr *wasPort)
  94.  
  95. {
  96.  if (wasPort != nil)
  97.   {
  98.     // Release the data in wasPort.portPixMap since
  99.     // ClosePort won't do it for us.
  100.    DisposePtr(((*wasPort)->portBits).baseAddr);
  101.     // And then, close wasPort.
  102.    ClosePort(*wasPort);
  103.    *wasPort = nil;
  104.   }
  105. }
  106.  
  107. There, that wasn‚Äôt so bad. Memory leaks plugged, you can Create and Kill offscreen pixmaps and bitmaps ‚Äòtill the cows come home, and we‚Äôre all happy campers.
  108. You‚Äôll find a new MGW1Code folder, containing the improved graphics file, in the folder named MGD4/96. The file is still named MGWGraphics1.c, but you‚Äôll note in its heading that it‚Äôs now version 1.0.1, and I‚Äôve added Kevin Teich‚Äôs name to the usual suspects list.
  109.  
  110. So tell me, what would you like to see in MGW2Code? Yes, a PPC Native version would be nice, I think all it would need there would be to change the callback routine in MGWSound1.c. Anything else? If you have a wish list, drop me a line at MacGameDev on AOL (that‚Äôs <macgamedev@aol.com> for  other service users) and we‚Äôll talk it over.
  111.  
  112. I‚Äôve also included a Universal Clam Resource (UCR) which you can use with any of the color HelloWorld projects (including GoodbyeWorld from the February ‚Äò96 IMG, though you‚Äôll have to change the GoodbyeWorldSnd resource ID# to 3010 instead of 3000), or mix and match to create your own. HelloWorld6.œÄ is included for those of you with CodeWarrior 6 or later, including the CodeWarrior Lite versions, but who tuned in late to this column. See you next month! I mean, SEE YOU NEXT MONTH!!!!
  113.